package org.karelun.whiteboard.ui.cdo.editor;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.emf.cdo.common.CDOCommonSession.Options.LockNotificationMode;
import org.eclipse.emf.cdo.eresource.CDOResource;
import org.eclipse.emf.cdo.session.CDOSessionLocksChangedEvent;
import org.eclipse.emf.cdo.transaction.CDOTransactionConflictEvent;
import org.eclipse.emf.cdo.transaction.CDOTransactionFinishedEvent;
import org.eclipse.emf.cdo.transaction.CDOTransactionFinishedEvent.Type;
import org.eclipse.emf.cdo.transaction.CDOTransactionStartedEvent;
import org.eclipse.emf.cdo.util.CDOUtil;
import org.eclipse.emf.cdo.view.CDOView;
import org.eclipse.emf.cdo.view.CDOViewInvalidationEvent;
import org.eclipse.emf.common.command.BasicCommandStack;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.edit.EMFEditPlugin;
import org.eclipse.emf.edit.provider.ComposedAdapterFactory;
import org.eclipse.emf.edit.provider.ReflectiveItemProviderAdapterFactory;
import org.eclipse.emf.edit.provider.resource.ResourceItemProviderAdapterFactory;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.net4j.util.event.IEvent;
import org.eclipse.net4j.util.event.IListener;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.views.properties.IPropertySheetPage;
import org.eclipse.ui.views.properties.tabbed.ITabbedPropertySheetPageContributor;
import org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetPage;
import org.karelun.whiteboard.Triangle;
import org.karelun.whiteboard.edit.factory.PartFactory;
import org.karelun.whiteboard.edit.factory.WhiteboardCreationFactory;
import org.karelun.whiteboard.editingdomain.cdo.ITransactionEditingDomain;
import org.karelun.whiteboard.editingdomain.cdo.TransactionAdapterFactoryEditingDomain;
import org.karelun.whiteboard.gef.DefaultEditDomain;
import org.karelun.whiteboard.gef.editparts.ScalableFreeformRootEditPart;
import org.karelun.whiteboard.gef.palette.CreationToolEntry;
import org.karelun.whiteboard.gef.palette.MarqueeToolEntry;
import org.karelun.whiteboard.gef.palette.PaletteDrawer;
import org.karelun.whiteboard.gef.palette.PaletteGroup;
import org.karelun.whiteboard.gef.palette.PaletteRoot;
import org.karelun.whiteboard.gef.palette.SelectionToolEntry;
import org.karelun.whiteboard.gef.palette.ToolEntry;
import org.karelun.whiteboard.gef.ui.parts.GraphicalEditorWithFlyoutPalette;
import org.karelun.whiteboard.ui.cdo.LockAction;
import org.karelun.whiteboard.ui.cdo.UnlockAction;

/**
 * @Author: KarelunHuang
 * @Date: Jan 23, 2013
 **/
public class CDOEditor extends GraphicalEditorWithFlyoutPalette implements ITabbedPropertySheetPageContributor{
	
	private ITransactionEditingDomain editingDomain;

	private ComposedAdapterFactory adapterFactory;
	
	private IPropertySheetPage propertySheetPage;
	
	private IListener sessionListener=new IListener() {
		
		public void notifyEvent(IEvent event) {
			if(event instanceof CDOSessionLocksChangedEvent){
				refreshGraphicalViewer();
				
			}
		}
	};
	
	private IListener cdoViewListener = new IListener() {
		public void notifyEvent(IEvent event) {
			if (event instanceof CDOViewInvalidationEvent) {
			
			} else if (event instanceof CDOTransactionStartedEvent) {

			}else if( event instanceof CDOTransactionFinishedEvent){
				cdoViewDirtyStateChanged();
				
				Type type = ((CDOTransactionFinishedEvent)event).getType();
				CDOView source = ((CDOTransactionFinishedEvent)event).getSource();
				if(type==Type.COMMITTED&&!source.equals(editingDomain.getCDOView())){
					//TODO: notify the current viewer that the content has been modified.
				}
			}else if (event instanceof CDOTransactionConflictEvent) {

			}
		}
	};
	
	public CDOEditor() {
	}
	
	@Override
	public void init(IEditorSite site, IEditorInput input)throws PartInitException {
		createEditingDomain(input);
		super.init(site, input);
	}
	
	private void createEditingDomain(IEditorInput input) {
		ComposedAdapterFactory.Descriptor.Registry registry = EMFEditPlugin.getComposedAdapterFactoryDescriptorRegistry();
		adapterFactory = new ComposedAdapterFactory(registry);
		adapterFactory.addAdapterFactory(new ResourceItemProviderAdapterFactory());
		adapterFactory.addAdapterFactory(new ReflectiveItemProviderAdapterFactory());
		
		CDOView cdoView=(CDOView)input.getAdapter(CDOView.class);
		cdoView.addListener(cdoViewListener);
		cdoView.getSession().addListener(sessionListener);
		cdoView.getSession().options().setLockNotificationMode(LockNotificationMode.ALWAYS);
		
		BasicCommandStack commandStack = new BasicCommandStack();
		editingDomain = new TransactionAdapterFactoryEditingDomain(adapterFactory, commandStack, cdoView);
		boolean isEditable=!cdoView.isClosed()&&!cdoView.isReadOnly();
		setEditDomain(new DefaultEditDomain(this,editingDomain,isEditable));
	}

	@Override
	protected void configureGraphicalViewer() {
		super.configureGraphicalViewer();
		ScalableFreeformRootEditPart rootEditPart = new ScalableFreeformRootEditPart();
		getGraphicalViewer().setRootEditPart(rootEditPart);
		getGraphicalViewer().setEditPartFactory(new PartFactory());
		
		MenuManager contextMenu = createContextMenu();
		getGraphicalViewer().setContextMenu(contextMenu);
		getSite().registerContextMenu(contextMenu, getGraphicalViewer());
	}
	
	private MenuManager createContextMenu() {
		MenuManager menu = new MenuManager("contextMenu"); //$NON-NLS-1$
		menu.add(getActionRegistry().getAction("lock"));
		menu.add(getActionRegistry().getAction("unlock"));
		return menu;
	}

	@Override
	protected void initializeGraphicalViewer() {
		EObject content = editingDomain.getResourceSet().getResources().get(0)
				.getContents().get(0);
		getGraphicalViewer().setContents(content);
	}

	@Override
	protected void createActions() {
		super.createActions();
		addSelectionAction(new LockAction(this));
		addSelectionAction(new UnlockAction(this));
	}
	
	@Override
	protected PaletteRoot getPaletteRoot() {
		PaletteRoot root = new PaletteRoot();
		PaletteGroup toolGroup = new PaletteGroup("Whiteboard");

		ToolEntry selectTool = new SelectionToolEntry();
		ToolEntry marqueTool = new MarqueeToolEntry();
		toolGroup.add(selectTool);
		toolGroup.add(marqueTool);

		root.setDefaultEntry(selectTool);
		root.add(toolGroup);

		PaletteDrawer drawer = new PaletteDrawer("Triangle");
		CreationToolEntry shapeEntry = new CreationToolEntry("Triangle", "",
				new WhiteboardCreationFactory(Triangle.class), null, null);
		drawer.add(shapeEntry);
		root.add(drawer);
		return root;
	}

	@Override
	public void doSave(IProgressMonitor monitor) {
		final Map<Object, Object> saveOptions = new HashMap<Object, Object>();
		saveOptions.put(Resource.OPTION_SAVE_ONLY_IF_CHANGED,
				Resource.OPTION_SAVE_ONLY_IF_CHANGED_MEMORY_BUFFER);
		IRunnableWithProgress operation = new IRunnableWithProgress() {
			public void run(IProgressMonitor monitor)
					throws InvocationTargetException, InterruptedException {
				ResourceSet resourceSet = editingDomain.getResourceSet();
				EList<Resource> resources = CDOUtil.getResources(resourceSet);
				monitor.beginTask("", resources.size()); //$NON-NLS-1$
				for (Resource resource : resources) {
					saveOptions.put(CDOResource.OPTION_SAVE_PROGRESS_MONITOR,
							new SubProgressMonitor(monitor, 1));
					saveOptions.put(
							CDOResource.OPTION_SAVE_OVERRIDE_TRANSACTION,editingDomain.getCDOView());
					try {
						resource.save(saveOptions);
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			}
		};
		try {
			new ProgressMonitorDialog(getSite().getShell()).run(true, true,
					operation);
			((BasicCommandStack) editingDomain.getCommandStack()).saveIsDone();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	private void cdoViewDirtyStateChanged() {
		getSite().getShell().getDisplay().asyncExec(new Runnable() {
			public void run() {
				firePropertyChange(IEditorPart.PROP_DIRTY);
			}
		});
	}
	
	private void refreshGraphicalViewer() {
		getSite().getShell().getDisplay().asyncExec(new Runnable(){
			public void run() {
				getGraphicalViewer().getContents().refresh();
			}
		});
		
	}

	@Override
	public boolean isDirty() {
		return editingDomain.getCDOView().isDirty();
	}
	
	@Override
	public Object getAdapter(Class type) {
		if(IPropertySheetPage.class==type){
			if(propertySheetPage==null||propertySheetPage.getControl().isDisposed()){
				propertySheetPage=new TabbedPropertySheetPage(this);
			}
			return propertySheetPage;
		}
		return super.getAdapter(type);
	}
	
	@Override
	public String getContributorId() {
		return "org.karelun.whiteboard.ui.propertyContributor"; //$NON-NLS-1$
	}
}
